package eu.connect.im;

import java.io.IOException;
import java.net.Socket;
import java.util.Locale;

import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolVersion;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.DefaultedHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

public class RequestHandleThread extends Thread
{
	private final IMCONNECTor connector;
	private final Socket insocket;
	private final DefaultHttpServerConnection inconn;
	private final HttpParams params;
    
	public RequestHandleThread(final IMCONNECTor connector, final Socket insocket) throws IOException
	{
		super();
		this.connector = connector;
		this.insocket = insocket; 
		this.params = new SyncBasicHttpParams();
        this.params
        	.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
        	.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
        	.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
        	.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
        	.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
		this.inconn = new DefaultHttpServerConnection();
		this.inconn.bind(this.insocket, this.params);
    }
    
	public void run()
	{
        //System.out.println("New connection thread");
		HttpContext context = new BasicHttpContext(null);
		context.setAttribute("http.connection", this.inconn);
        
		try
		{
			while (!Thread.interrupted())
			{
				HttpResponse response = null;
				HttpRequest request = this.inconn.receiveRequestHeader();
				request.setParams(new DefaultedHttpParams(request.getParams(), this.params));
				ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
				if (!ver.lessEquals(HttpVersion.HTTP_1_1))
				{
					ver = HttpVersion.HTTP_1_1;
				}
				if ((request instanceof HttpEntityEnclosingRequest))
				{
					this.inconn.receiveRequestEntity((HttpEntityEnclosingRequest)request);
				}
        		
				response = new DefaultHttpResponseFactory().newHttpResponse(ver, 200, context);
				response.setParams(new DefaultedHttpParams(response.getParams(), this.params));
                
				HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
						new ResponseDate(),
						new ResponseServer(),
						new ResponseContent(),
						new ResponseConnControl()
				});

				context.setAttribute("http.request", request);
				context.setAttribute("http.response", response);

				httpproc.process(request, context);
				
				int port = 80;
                String URL = request.getRequestLine().getUri();
                String host = URL;
                
                int n = URL.indexOf("//");
         		if (n != -1)
                    host = URL.substring(n+2);
          		n = host.indexOf('/');
           		if (n != -1) 	
           			host = host.substring(0,n);
            	    
          		n = host.indexOf(':');
           		if (n != -1) { 
           			port = Integer.parseInt(host.substring(n+1));
           			host=host.substring(0,n);
          		}
           		
           		String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
           		if (method.equals("GET") || method.equals("POST"))
            	{
           			HTTPClientProxyThread httpClientProxy = new HTTPClientProxyThread(request, response, this.connector.queue);
           			HTTPServerProxyThread httpServerProxy = new HTTPServerProxyThread(host, port, this.connector.queue);
           			httpClientProxy.setCorrepondingPartner(httpServerProxy);
           			httpServerProxy.setCorrepondingPartner(httpClientProxy);
           			httpClientProxy.start();
           			while (httpClientProxy.isAlive())
           			{
           				try
           				{
           					Thread.sleep(1000);
           				}
           				catch(InterruptedException e){}
           			}
           			
           			httpproc.process(response, context);
           		    this.inconn.sendResponseHeader(response);
           		    this.inconn.sendResponseEntity(response);
           		    this.inconn.flush();
           			break;
            	}
           		else if (method.equals("CONNECT"))
           		{
           			HttpHost target = new HttpHost(host,port);
            		Socket outsocket = null;
            		try
            		{
            			outsocket = new Socket(target.getHostName(), target.getPort());
            		}
            		catch (Exception e){}
            		
            		if (outsocket == null)
            		{
            			response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
            			httpproc.process(response, context);
            			this.inconn.sendResponseHeader(response);
            		    this.inconn.sendResponseEntity(response);
            			break;
            		}
            		
            		response.setStatusCode(HttpStatus.SC_OK);
            		response.addHeader("Connection", "Keep-Alive");
            		httpproc.process(response, context);
        			this.inconn.sendResponseHeader(response);
        		    this.inconn.sendResponseEntity(response);
        		    this.inconn.flush();
        		    
        		    this.insocket.setKeepAlive(true);
        		    this.insocket.setSoTimeout(100000);
        		    outsocket.setKeepAlive(true);
        		    outsocket.setSoTimeout(100000);
        		    
        		    XMPPProxyThread xmppClient = new XMPPProxyThread("client", insocket, this.connector.queue);
                	XMPPProxyThread xmppServer = new XMPPProxyThread("server", outsocket, this.connector.queue);
                	xmppClient.setDaemon(true);
                	xmppServer.setDaemon(true);
                	xmppClient.start();
                	xmppServer.start();
                	this.connector.xmppClient = xmppClient;
                	this.connector.xmppServer = xmppServer;
        		    
                	while (xmppClient.isRunning() && xmppServer.isRunning())
           			{
           				try
           				{
           					Thread.sleep(1000);
           				}
           				catch(InterruptedException e){}
           			}
                	
                	xmppClient.shutdown();
                	xmppServer.shutdown();
                	outsocket.close();
                	break;
        		    
           		}
            }
        } catch (ConnectionClosedException ex) {
            System.err.println("Client closed connection");
        } catch (IOException ex) {
            System.err.println("I/O error: " + ex.getMessage());
        } catch (HttpException ex) {
            System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
        } finally {
        	try {
                this.inconn.shutdown();
            } catch (IOException ignore) {}
        }
    }

}
